// Copyright (c) 2024, the Dart project authors.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:io';

import 'third_party/generated_bindings.dart';

// Napi error codes
enum NapiErrorCode {
  OK(0),
  ERR(1),
  THREAD_DETACHED(2),
  VERSION_ERROR(3),
  OUT_OF_MEMORY(4),
  SINGLETON_EXISTS(5),
  INVALID_ARGS(6);

  const NapiErrorCode(this.value);
  final int value;
}

final class UseAfterReleaseError extends StateError {
  UseAfterReleaseError() : super('Use after release error');
}

final class JNullError extends StateError {
  JNullError() : super('The reference was null');
}

final class NapiNullError extends StateError {
  NapiNullError() : super('The Napi reference was null');
}

final class DoubleReleaseError extends StateError {
  DoubleReleaseError() : super('Double release error');
}

sealed class NapiError extends Error {
  static final _errors = {
    NapiErrorCode.ERR: NapiGenericError.new,
    NapiErrorCode.THREAD_DETACHED: NapiThreadDetachedError.new,
    NapiErrorCode.VERSION_ERROR: NapiVersionError.new,
    NapiErrorCode.OUT_OF_MEMORY: NapiOutOfMemoryError.new,
    NapiErrorCode.SINGLETON_EXISTS: NapiVmExistsError.new,
    NapiErrorCode.INVALID_ARGS: NapiArgumentError.new,
  };

  final String message;

  NapiError(this.message);

  factory NapiError.of(NapiErrorCode status) {
    if (!_errors.containsKey(status)) {
      status = NapiErrorCode.ERR;
    }
    return _errors[status]!();
  }

  @override
  String toString() => 'NapiError: $message';
}

final class NapiGenericError extends NapiError {
  NapiGenericError() : super('Generic NAPI error');
}

final class NapiThreadDetachedError extends NapiError {
  NapiThreadDetachedError() : super('Thread detached from VM');
}

final class NapiVersionError extends NapiError {
  NapiVersionError() : super('NAPI version error');
}

final class NapiOutOfMemoryError extends NapiError {
  NapiOutOfMemoryError() : super('Out of memory');
}

final class NapiVmExistsError extends NapiError {
  NapiVmExistsError() : super('VM Already created');
}

final class NapiArgumentError extends NapiError {
  NapiArgumentError() : super('Invalid arguments');
}

final class NoJsvmInstanceError extends Error {
  @override
  String toString() => 'No NAPI instance is available';
}

final class NoNapiInstanceError extends Error {
  @override
  String toString() => 'No NAPI instance is available';
}

class NapiException implements Exception {
  final String message;
  final String stackTrace;
  NapiException(this.message, this.stackTrace);
  @override
  String toString() => 'Exception in Ark/N-API code: $message\n\n$stackTrace\n';
}

final class HelperNotFoundError extends Error {
  final String path;
  HelperNotFoundError(this.path);

  @override
  String toString() => '''
Unable to locate the helper library.

Ensure that the helper library is available at the path: $path
Run the appropriate setup to generate the shared library if it does not exist.

Note: If a custom build path is used, set the same in NAPI spawn and in new isolates.
''';
}

final class DynamicLibraryLoadError extends Error {
  final String libraryPath;
  DynamicLibraryLoadError(this.libraryPath);

  @override
  String toString() {
    return '''
Failed to load dynamic library at path: $libraryPath
The library was found at the specified path, but it could not be loaded. 
This might be due to missing dependencies or incorrect file permissions. 
Please ensure ${Platform.isWindows ? r'that `\bin\server\jvm.dll` is in the PATH, and ' : ''}that the file has the correct permissions.

''';
  }
}


